﻿using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Mrkz.ImageManipulation
{
    public static class InteropImage<TPixel>
      where TPixel : IArithmeticOperation<TPixel>, IEquatable<TPixel>, IEquatableWithTolerance<TPixel>, IPixel, new()
    {
        public static void WritePixels(TImage<TPixel> src, WriteableBitmap DestinationImage)
        {
            InteropPixel[,] _data = new InteropPixel[src.CropArea.Height, src.CropArea.Width];

            for (int r = 0; r < src.CropArea.Height; r++)
                for (int c = 0; c < src.CropArea.Width; c++)
                {
                    TPixel pix = src.GetPixel(r, c);

                    _data[r, c].a = (byte)pix.Alpha;
                    _data[r, c].r = (byte)pix.Red;
                    _data[r, c].g = (byte)pix.Green;
                    _data[r, c].b = (byte)pix.Blue;
                    
                }

            int stride = (src.CropArea.Width * 32 + 7) / 8;

            GCHandle p = GCHandle.Alloc(_data, GCHandleType.Pinned);
            
            DestinationImage.WritePixels(new Int32Rect(0, 0, src.CropArea.Width, src.CropArea.Height),
                p.AddrOfPinnedObject(),
                src.CropArea.Width * src.CropArea.Height * 4, stride);

            p.Free();
        }

        /// <summary>
        /// Write the complex image into two multichannel real images that can be showed
        /// </summary>
        /// <param name="DestinationImage"></param>
        public static void WritePixels(TImage<ComplexPixel> InputImage, WriteableBitmap DestinationImageMagnitude, WriteableBitmap DestinationImagePhase)
        {
            InputImage.Normalize();

            //create the pixel array for phase plot
            InteropPixel[,] pixels = new InteropPixel[InputImage.CropArea.Height, InputImage.CropArea.Width];

            for (int i = 0; i < InputImage.CropArea.Height; i++)
                for (int j = 0; j < InputImage.CropArea.Width; j++)
                {
                    pixels[i, j].a = 255;
                    pixels[i, j].r = (byte)(Math.Log(1 + InputImage._data[i, j].CRed.Phase) * 255);
                    pixels[i, j].g = (byte)(Math.Log(1 + InputImage._data[i, j].CGreen.Phase) * 255);
                    pixels[i, j].b = (byte)(Math.Log(1 + InputImage._data[i, j].CBlue.Phase) * 255);
                }
            GCHandle pinnedPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);

            int _stride = (InputImage.CropArea.Width * 32 + 7) / 8;

            DestinationImagePhase.WritePixels(new Int32Rect(0, 0, InputImage.CropArea.Width, InputImage.CropArea.Height),
                pinnedPixels.AddrOfPinnedObject(),
                InputImage.CropArea.Width * InputImage.CropArea.Height * 4, _stride);

            pinnedPixels.Free();

            //create the pixel array for magnitude plot
            pixels = new InteropPixel[InputImage.CropArea.Height, InputImage.CropArea.Width];

            for (int i = 0; i < InputImage.CropArea.Height; i++)
                for (int j = 0; j < InputImage.CropArea.Width; j++)
                {
                    pixels[i, j].a = 255;
                    pixels[i, j].r = (byte)(Math.Log(1 + InputImage._data[i, j].CRed.Magnitude) * InputImage._magn_norm_factor * 255);
                    pixels[i, j].g = (byte)(Math.Log(1 + InputImage._data[i, j].CGreen.Magnitude) * InputImage._magn_norm_factor * 255);
                    pixels[i, j].b = (byte)(Math.Log(1 + InputImage._data[i, j].CBlue.Magnitude) * InputImage._magn_norm_factor * 255);

                }
            pinnedPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);

            DestinationImageMagnitude.WritePixels(new Int32Rect(0, 0, InputImage.CropArea.Width, InputImage.CropArea.Height),
                pinnedPixels.AddrOfPinnedObject(),
                 InputImage.CropArea.Width * InputImage.CropArea.Height * 4, _stride);

            pinnedPixels.Free();

        }

        public static InteropPixel[,] CopyPixels(BitmapSource SourceImage)
        {
            int _width = SourceImage.PixelWidth;
            int _height = SourceImage.PixelHeight;

            if (SourceImage.Format != PixelFormats.Bgra32)
                SourceImage = new FormatConvertedBitmap(SourceImage, PixelFormats.Bgra32, null, 0);

            InteropPixel[,] pixels = new InteropPixel[SourceImage.PixelHeight, SourceImage.PixelWidth];

            GCHandle pinnedPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
            int _stride = (_width * SourceImage.Format.BitsPerPixel + 7) / 8;

            SourceImage.CopyPixels(
              new Int32Rect(0, 0, _width, _height),
              pinnedPixels.AddrOfPinnedObject(),
              _width * _height * 4,
                  _stride);

            pinnedPixels.Free();

            return pixels;
        }
    }
}
